/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jackal;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

class EnemyLevel {
    public static final int LEVEL_EASY = 0;
    public static final int LEVEL_MEDIUM = 1;
    public static final int LEVEL_HARD = 2;
}

class WayArrayLists {
	private ArrayList<ArrayList<Point>> ways;
	
	WayArrayLists () {
		ways = new ArrayList<ArrayList<Point>>();
	}
	
	public void addWay(ArrayList<Point> newWay) {
		ways.add(newWay);
	}
	public int getSize() {
		return ways.size();
	}
	public ArrayList<Point> getWay(int i) {
		return ways.get(i);
	}
}

/**
 *
 * @author pisklenovav
 */
public class EnemyLogic {
    private int level = EnemyLevel.LEVEL_EASY;
    private WayArrayLists wayArrayLists = new WayArrayLists ();
    
    EnemyLogic(int level) {
        this.level = level;
    }

    private ArrayList<Point> getAvaliblePointsFor(Point start, Gamer.Pirate pirate, 
    		World world, int maxDistance) {
    	ArrayList<Point> avaliblePoints = new ArrayList<>();
    	
    	for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
            	// обходим воду
                if (world.island.getPlace(i, j).getType() == BasicObj.WATER) {
                	continue;
                }
                
                // если мы с монетой - обходим не перевернутые
                if (pirate.haveMoney && !world.island.getPlace(i, j).reversed) {
                	continue;
                }
                
                // удаляем из доступных точку в которой уже стоит пират
                if (world.island.getPlace(i, j).getCoord().equals(start)) {
                	continue;
                }
                
                // если уровень сложности средний или высокий - обходим стрелки
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_ARROW &&
                		(level == EnemyLevel.LEVEL_HARD || level == EnemyLevel.LEVEL_MEDIUM)) {
                	continue;
                }
                // если уровень сложности средний или высокий - обходим болота
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_SLOWDOWN &&
                		(level == EnemyLevel.LEVEL_HARD || level == EnemyLevel.LEVEL_MEDIUM)) {
                	continue;
                }
                // если уровень сложности средний или высокий - обходим пушки
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_CANNON &&
                		(level == EnemyLevel.LEVEL_HARD || level == EnemyLevel.LEVEL_MEDIUM)) {
                	continue;
                }
                
                if (Island.getDistanceFromTo(start, new Point(i, j)) > maxDistance) {
                	continue;
                }
                   
                avaliblePoints.add(new Point(i, j));
            }
        }
    	
    	return avaliblePoints;
    }
    
    //ArrayList<Point> bestWayList;
    private void createBestWay(Gamer.Pirate pirate, Point start, Point end, 
    		World world, ArrayList<Point> existWay) {
    	
    	ArrayList<Point> tempWay = new ArrayList<Point>(existWay);
    	tempWay.add(start);
    	
    	ArrayList<Point> distance1 = getAvaliblePointsFor(start, pirate, world, 1);
    	
    	for (int i=0; i<tempWay.size(); i++) {  
    		int n=0;
    		while (n<distance1.size()) {
    			if (distance1.get(n).equals(tempWay.get(i))) {
    				distance1.remove(n);
    				continue;
    			}
    			n++;    		
    		}
    	}
    	
    	for (int i=0; i<distance1.size(); i++) {  		
    		if (distance1.get(i).equals(end)) {
    			tempWay.add(distance1.get(i));
    			
    			wayArrayLists.addWay(tempWay);
    			continue;
    		}
    		
    		createBestWay(pirate, distance1.get(i), end, world, tempWay);
    	}
    }
    
    
    private ArrayList<Point> getAvaliblePointsWithMoney(Gamer.Pirate pirate, World world){
        ArrayList<Point> avaliblePoints = new ArrayList<>();

        avaliblePoints.add(pirate.gamer().boat.getCoord()); // добавляем координаты своей лодки

        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                if (world.island.getPlace(i, j).getType() != BasicObj.WATER &&
                    world.island.getPlace(i, j).getType() != BasicObj.SPEC_ARROW &&
                    !world.island.getPlace(i, j).getCoord().equals(pirate.getCoord()) &&
                        world.island.getPlace(i, j).reversed) {
                        avaliblePoints.add(world.island.getPlace(i, j).getCoord());
                }
            }
        }
        
        return avaliblePoints;
    }

    private void smoothlyUpPriceOfPoint(int[][] pointArr, Point point) {
        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                if (Island.getDistanceFromTo(point, new Point(i,j)) == 1) {
                    pointArr[i][j] += 2;
                }
                if (Island.getDistanceFromTo(point, new Point(i,j)) == 2) {
                    pointArr[i][j] += 1;
                }
            }
        }
    }

    private ArrayList<Point> getVisiblePoints(Gamer.Pirate pirate, World world){
        int[][] priceOfPoints = new int[Island.BOARD_SIZE][Island.BOARD_SIZE];// = new Place[BOARD_SIZE][BOARD_SIZE];

        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                priceOfPoints[i][j] = 0;
            }
        }

        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                if (world.island.getPlace(i, j).getType() == BasicObj.WATER) {
                    priceOfPoints[i][j] -= 1;
                }
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_CHEST &&
                        ((Chest) world.island.getPlace(i, j)).getGoldCount() > 0) {
                    priceOfPoints[i][j] += 10;
                    
                    // если уровень сложности выше легкого - учим врага быстрее находить сундуки
                    if (this.level == EnemyLevel.LEVEL_MEDIUM || 
                            this.level == EnemyLevel.LEVEL_HARD) {
                        smoothlyUpPriceOfPoint(priceOfPoints, new Point(i, j));
                    }
                }
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_SLOWDOWN &&
                        this.level != EnemyLevel.LEVEL_EASY) {
                    priceOfPoints[i][j] -= 2;
                }
                if (world.island.getPlace(i, j).getType() == BasicObj.JUNGLE &&
                        !world.island.getPlace(i, j).reversed) {
                    priceOfPoints[i][j] += 1;
                }
            }
        }

        ArrayList<Point> bestPoints = new ArrayList<>();

        int max = Integer.MIN_VALUE;
        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                if (pirate.getDistanceTo(world.island.getPlace(i, j).getCoord()) == 1) {

                    if (priceOfPoints[i][j] > max) {
                        max = priceOfPoints[i][j];
                        bestPoints.clear();
                        bestPoints.add(new Point(i,j));
                    }
                    if (priceOfPoints[i][j] == max) {
                        bestPoints.add(new Point(i,j));
                    }
                    //visiblePoints.add(world.island.getPlace(i, j).getCoord());
                }
            }
        } 

        return bestPoints;
    }

    
    /**
     * Ищем путь 
     * 
     * @param pirate от указанного пирата 
     * @param end до указанной точки 
     * @param world ну и передаем ссылку на весь мир 
     */
    private ArrayList<Point> findGoodWay(Gamer.Pirate pirate, World world, Point end){
        ArrayList<Point> avaliblePoints = getAvaliblePointsWithMoney(pirate, world);
        ArrayList<Point> result = new ArrayList<>();
        Point start = pirate.getCoord();
        
        if (Island.getDistanceFromTo(start, end) == 1) {
            result.add(end);
            return result;
        }
      
        ArrayList<Point> canMovePoints = new ArrayList<>();
        for (int i = 0; i < avaliblePoints.size(); i++) {
            if (Island.getDistanceFromTo(start, avaliblePoints.get(i))  == 1) {
                canMovePoints.add(avaliblePoints.get(i));
            }
        }

        for (int i=0; i<canMovePoints.size(); i++){
            ArrayList<Point> goodWay = new ArrayList<>();
            boolean isNext = isSearchNextPoint(canMovePoints.get(i), end, avaliblePoints, goodWay);
            if (isNext) {
                System.out.println(goodWay.toString());  
                return goodWay;
            }
        }
        
        return result;
    }
    
    /*
     * Функция добавляет точку к списку пути goodway, если из этой точки дальше есть удачный путь
     */
    private boolean isSearchNextPoint(Point start, Point end, ArrayList<Point> availablePoints, ArrayList<Point> goodWay) {
        ArrayList<Point> myAvailablePoints = availablePoints;
        
        goodWay.add(start); // сначала подразумеваем, что точка удачная и из нее удастся найти путь
                
        if (Island.getDistanceFromTo(start, end) == 0) { // мы пришли
            return true;
        }
        
        // удаляем точку из которой мы пришли из списка доступных точек, чтобы гне ходить кругами
        int n = 0;
        while (n < myAvailablePoints.size()) { 
            if (myAvailablePoints.get(n).equals(start)){
                myAvailablePoints.remove(n);
                break;
            }
            n++;
        }

        // составляем список точек куда мы можем походить сейчас
        // а это все, что не дальше одной клетки
        ArrayList<Point> canMovePoints = new ArrayList<>();
        for (int i = 0; i < myAvailablePoints.size(); i++) {
            if (Island.getDistanceFromTo(start, myAvailablePoints.get(i))  == 1) {
                canMovePoints.add(myAvailablePoints.get(i));
            }
        }
        
        // если найдены досигаемые точки, ищем среди них ту, которая ближе к нашей цели
        // так эффективнее
        if (canMovePoints.size() > 1) {
            // сортировка пузырьком
            for (int i = 1; i < canMovePoints.size(); i++) {
                for (int j = 1; j < canMovePoints.size(); j++) {
                    if (Island.getDistanceFromTo(canMovePoints.get(j-1), end) > 
                            Island.getDistanceFromTo(canMovePoints.get(j), end)) {
                        Point point = canMovePoints.get(j);
                        canMovePoints.set(j, canMovePoints.get(j-1));
                        canMovePoints.set(j-1, point);
                    }
                }
            }
        }
        
        // а теперь для всех доступных точек рекурсивно вызываем поиск пути
        for (int i=0; i<canMovePoints.size(); i++){
            boolean mustExit = isSearchNextPoint(canMovePoints.get(i), end, myAvailablePoints, goodWay);
            if (mustExit) {
                return true;
            }
        }
         
        // если выполнение дошло до этой строки, значит к цели по этому маршруту мы прийти не сможем
        // удаляем эту точку из маршрута, так как она тупиковая
        goodWay.remove(goodWay.size()-1);
        return false;
    }    
    
    public void enemyPirateMove(Gamer.Pirate pirate, World world) {
        if (pirate.haveMoney || !pirate.way.isEmpty()) {
            if (pirate.way.isEmpty()) {
                ArrayList<Point> wayPointList = findGoodWay(pirate, world, pirate.gamer().boat.getCoord());
                
                createBestWay(pirate, pirate.getCoord(), pirate.gamer().boat.getCoord(), world, new ArrayList<Point>());
                System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!! "+wayArrayLists.getSize());
                //wayArrayLists
                if (wayPointList.isEmpty()) {
                    pirate.dropMoney();
                    wayPointList = findGoodWay(pirate, world, pirate.gamer().boat.getCoord());
                } else {
                    pirate.way = wayPointList;
                }     
                
            }
            
            //System.out.println("Пират идет к цели, осталось "+pirate.way.size());
            
            if (!pirate.way.isEmpty()){
                pirate.goNextWayPoint(world);
            }
                  
        } else {
            if (isSearchSomeoneHit(pirate, world)) { // ищем врагов вокруг
                print("[Враг] есть кого ударить");
                searchSomeoneHit(pirate, world);
            } else if (isSearchChest(pirate, world)) { // ищем сундуки
                print("[Враг] иду к сундуку");
                searchChest(pirate, world);
            } else {
                print("[Враг] слоняюсь без дела");
                searchNextRandomPoint(pirate, world);
            }

            if (!pirate.way.isEmpty()){
                pirate.goNextWayPoint(world);
            }
        }
    }

    private int calcBenefitOfMove(Gamer.Pirate pirate, World world) {
        int benefit = 0;

        if (!pirate.alive) { // если пират мертв
            benefit = -1;
        } else if (isSearchSomeoneHit(pirate, world)) { // если рядом враг
            benefit = 100;
        } else if (pirate.haveMoney) { // если в руках денги
            benefit = 90;
        } else if (isSearchChest(pirate, world)) { // если есть вилимые сундуки
            benefit = 80;
        } //else {

        return benefit;
    }

    /*
     * Случайно выбираем следующую точку в маршруте
     */
    private void searchNextRandomPoint(Gamer.Pirate pirate, World world) {
        //System.out.println("- Слоняемся без дела");

        ArrayList<Point> availablePoints = getVisiblePoints(pirate, world);

//        for (int i = 0; i < Island.BOARD_SIZE; i++) {
//            for (int j = 0; j < Island.BOARD_SIZE; j++) {
//                if (pirate.canMoveTo(world.island.getPlace(i, j)) &&
//                        world.island.getPlace(i, j).getType() != BasicObj.WATER &&
//                        !pirate.getCoord().equals(world.island.getPlace(i, j).getCoord())) {
//                    availablePoints.add(new Point(i,j));
//
//                    // движения пирата хаотичны, но мы увеличиваем шанс того, что пират пойдет в еще не изведанные джунгли
//                    if (!world.island.getPlace(i, j).reversed) {
//                        availablePoints.add(new Point(i,j));
//                    }
//                }
//            }
//        }
        
        int n = (int)Math.floor(Math.random() * availablePoints.size());
        pirate.way.add(availablePoints.get(n));
    }

    /*
     * Ищем сундуки
     */
    private boolean isSearchChest(Gamer.Pirate pirate, World world) {
        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_CHEST &&
                    world.island.getPlace(i, j).reversed &&
                        ((Chest) world.island.getPlace(i, j)).getGoldCount() > 0) {

                    ArrayList<Point> wayPointList = findGoodWay(pirate, world, world.island.getPlace(i, j).getCoord());
                    if (wayPointList.size()>0) {
                        return true;
                    }
                }
            }
        }

        return false;
    }
    private void searchChest(Gamer.Pirate pirate, World world) {
        ArrayList<Point> visibleChests = new ArrayList<>();
        for (int i = 0; i < Island.BOARD_SIZE; i++) {
            for (int j = 0; j < Island.BOARD_SIZE; j++) {
                if (world.island.getPlace(i, j).getType() == BasicObj.SPEC_CHEST &&
                    world.island.getPlace(i, j).reversed && ((Chest) world.island.getPlace(i, j)).getGoldCount() > 0) {
                        visibleChests.add(new Point(i,j));
                }
            }
        }

        for (int i = 1; i < visibleChests.size(); i++) {
            for (int j = 1; j < visibleChests.size(); j++) {
                if (Island.getDistanceFromTo(visibleChests.get(j-1), pirate.getCoord()) > 
                        Island.getDistanceFromTo(visibleChests.get(j), pirate.getCoord())) {
                    Point point = visibleChests.get(j);
                    visibleChests.set(j, visibleChests.get(j-1));
                    visibleChests.set(j-1, point);
                }
            }
        }

        if (visibleChests.size() > 0) { // если есть видимые сундуки, они в приоритете
            //System.out.println("+ Есть видимые сундуки");
            for (int i=0; i<visibleChests.size(); i++) {
                ArrayList<Point> wayPointList = findGoodWay(pirate, world, visibleChests.get(i)); 
                if (wayPointList.size()>0) {
                    //System.out.println("+++ Есть сундук до которого можно дойти");
                    pirate.way = wayPointList;
                    break;
                }
            }
        } 
    }


    /*
     * Ищем кого бы ударить
     */
    private boolean isSearchSomeoneHit(Gamer.Pirate pirate, World world) {
        Gamer enemyGamer = world.getEnemyGamerFor(pirate.gamer());
        
        if (pirate.getDistanceTo(enemyGamer.pirate1.getCoord()) == 1 &&
            !enemyGamer.pirate1.getCoord().equals(enemyGamer.boat.getCoord())) {
            return true;
        }
        if (pirate.getDistanceTo(enemyGamer.pirate2.getCoord()) == 1 &&
            !enemyGamer.pirate2.getCoord().equals(enemyGamer.boat.getCoord())) {
            return true;
        }

        return false;
    }

    private void searchSomeoneHit(Gamer.Pirate pirate, World world) {
        Gamer enemyGamer = world.getEnemyGamerFor(pirate.gamer());
        
        // TODO надо будет переделать дял поиска всех игроков
        if (pirate.getDistanceTo(enemyGamer.pirate1.getCoord()) == 1 &&
                !enemyGamer.pirate1.getCoord().equals(enemyGamer.boat.getCoord())) {
            pirate.way.clear();
            pirate.way.add(enemyGamer.pirate1.getCoord());
            pirate.dropMoney();
        } else if (pirate.getDistanceTo(enemyGamer.pirate2.getCoord()) == 1 &&
                !enemyGamer.pirate2.getCoord().equals(enemyGamer.boat.getCoord())) {
            pirate.way.clear();
            pirate.way.add(enemyGamer.pirate2.getCoord());
            pirate.dropMoney();
        }
    }


    public void move(Gamer gamer, World world){
        int bp1 = calcBenefitOfMove(gamer.pirate1, world);
        int bp2 = calcBenefitOfMove(gamer.pirate2, world);

        //System.out.println("bp1 "+bp1);
        //System.out.println("bp2 "+bp2);

        if (bp1>bp2) {
            enemyPirateMove(gamer.pirate1, world);
        } else if (bp2>bp1) {
            enemyPirateMove(gamer.pirate2, world);
        } else {
            int n = (int)Math.floor(Math.random() * 2);
            if (n==0) {
                enemyPirateMove(gamer.pirate1, world);
            } else {
                enemyPirateMove(gamer.pirate2, world);
            }
        }
    }

    private void print(String str) {
        System.out.println(str);
    }

}
